home *** CD-ROM | disk | FTP | other *** search
/ CICA 1993 April / CICA MS Windows - April 1993.iso / unzipped / util / mews11 / main.c < prev    next >
C/C++ Source or Header  |  1992-07-10  |  26KB  |  1,121 lines

  1. /*
  2.  *    MicroEMACS 3.11
  3.  *        written by Daniel M. Lawrence
  4.  *        based on code by Dave G. Conroy.
  5.  *
  6.  *    (C)Copyright 1988,1989,1990,1991 by Daniel M. Lawrence
  7.  *    MicroEMACS 3.11 can be copied and distributed freely for any
  8.  *    non-commercial purposes. MicroEMACS 3.11 can only be incorporated
  9.  *    into commercial software with the permission of the current author.
  10.  *
  11.  * This file contains the main driving routine, and some keyboard processing
  12.  * code, for the MicroEMACS screen editor.
  13.  *
  14.  */
  15.  
  16. #include    <stdio.h>
  17.  
  18. /* make global definitions not external */
  19. #define maindef
  20.  
  21. #include    "estruct.h"    /* global structures and defines */
  22. #include    "eproto.h"    /* variable prototype definitions */
  23. #include    "efunc.h"    /* function declarations and name table */
  24. #include    "edef.h"    /* global definitions */
  25. #include    "elang.h"    /* human language definitions */
  26. #include    "ebind.h"    /* default key bindings */
  27.  
  28. /* for many different systems, increase the default stack space */
  29.  
  30. #if    MSDOS && MSC
  31. #if     _MSC_VER < 700
  32. unsigned _stackavail = 20000;
  33. #endif
  34. #endif
  35.  
  36. #if    MSDOS && LATTICE
  37. unsigned _stack = 20000;
  38. #endif
  39.  
  40. #if    MSDOS && (DTL | ZTC)
  41. int    _okbigbuf = 0;        /* Only allocate memory when needed.*/
  42. int    _stack = 20000;     /* Reset the ol' stack size.*/
  43. #endif
  44.  
  45. #if    TOS && MWC
  46. long _stksize = 20000L;     /* reset stack size (must be even) */
  47. #endif
  48.  
  49. #if    MSDOS && AZTEC
  50. int _STKSIZ = 20000/16;     /* stack size in paragraphs */
  51. int _STKRED = 1024;        /* stack checking limit */
  52. int _HEAPSIZ = 4096/16;     /* (in paragraphs) */
  53. /*int _STKLOW = 0;        default is stack above heap (small only) */
  54. #endif
  55.  
  56. #if    MSDOS && TURBO
  57. extern unsigned int _stklen = 10000;
  58. #endif
  59.  
  60. /*    make VMS happy...    */
  61.  
  62. #if    VMS
  63. #include    <ssdef.h>
  64. #define GOOD    (SS$_NORMAL)
  65. #endif
  66.  
  67. #ifndef GOOD
  68. #define GOOD    0
  69. #endif
  70.  
  71. /*
  72.     This is the primary entry point that is used by command line
  73.     invocation, and by applications that link with microemacs in
  74.     such a way that each invocation of Emacs is a fresh environment.
  75.  
  76.     There is another entry point in VMS.C that is used when
  77.     microemacs is "linked" (In quotes, because it is a run-time link
  78.     rather than a link-time link.) with applications that wish Emacs
  79.     to preserve it's context across invocations.  (For example,
  80.     EMACS.RC is only executed once per invocation of the
  81.     application, instead of once per invocation of Emacs.)
  82.  
  83.     Note that re-entering an Emacs that is saved in a kept
  84.     subprocess would require a similar entrypoint.
  85. */
  86.  
  87. #if    CALLED
  88. emacs(argc, argv)
  89. #else
  90. main(argc, argv)
  91. #endif
  92.  
  93. int argc;    /* # of arguments */
  94. char *argv[];    /* argument strings */
  95.  
  96. {
  97.     register int status;
  98.  
  99.     /* Initialize the editor */
  100.     eexitflag = FALSE;
  101. #if     !WINDOW_MSWIN
  102.     vtinit();        /* Terminal */
  103. #endif
  104.  
  105.     if (eexitflag)
  106.         goto abortrun;
  107.     edinit(mainbuf);      /* Buffers, windows, screens */
  108.  
  109.     varinit();        /* user variables */
  110.     initchars();        /* character set definitions */
  111.  
  112.     /* Process the command line and let the user edit */
  113. #if    VMS
  114.     expandargs(&argc, &argv);    /* expand VMS wildcards.*/
  115. #endif
  116.     dcline(argc, argv, TRUE);
  117.     status = editloop();
  118. abortrun:
  119.     vttidy();
  120. #if    CLEAN
  121.     clean();
  122. #endif
  123. #if    CALLED
  124.     return(status);
  125. #else
  126.     exit(status);
  127. #endif
  128. }
  129.  
  130. #if    CLEAN
  131. /*
  132.     On some primitive operation systems, and when emacs is used as
  133.     a subprogram to a larger project, emacs needs to de-alloc its
  134.     own used memory, otherwise we just exit.
  135. */
  136.  
  137. PASCAL NEAR clean()
  138. {
  139.     register BUFFER *bp;    /* buffer list pointer */
  140.     register SCREEN *scrp;    /* ptr to screen to dump */
  141.  
  142.     /* first clean up the screens */
  143.     scrp = first_screen;
  144.     while (scrp) {
  145.         first_screen = sp->s_next_screen;
  146.         free_screen(scrp);
  147.         scrp = first_screen;
  148.     }
  149.     wheadp = NULL;
  150.  
  151.     /* then the buffers */
  152.     bp = bheadp;
  153.     while (bp) {
  154.         bp->b_nwnd = 0;
  155.         bp->b_flag = 0; /* don't say anything about a changed buffer! */
  156.         zotbuf(bp);
  157.         bp = bheadp;
  158.     }
  159.  
  160.     /* and the kill buffers */
  161.     clear_ring();
  162.  
  163.     /* clear some search variables */
  164. #if    MAGIC
  165.     mcclear();
  166.     rmcclear();
  167. #endif
  168.     if (patmatch != NULL) {
  169.         free(patmatch);
  170.         patmatch = NULL;
  171.     }
  172.  
  173.     /* dealloc the user variables */
  174.     varclean();
  175.  
  176.     /* and the video buffers */
  177.     vtfree();
  178. }
  179. #endif
  180.  
  181. /*    Process a command line.   May be called any time.    */
  182.  
  183. PASCAL NEAR dcline(argc, argv, firstflag)
  184.  
  185. int argc;
  186. char *argv[];
  187. int firstflag;    /* is this the first time in? */
  188.  
  189. {
  190.     register BUFFER *bp;        /* temp buffer pointer */
  191.     register int    firstfile;    /* first file flag */
  192.     register int    carg;        /* current arg to scan */
  193.     register int    startflag;    /* startup executed flag */
  194.     BUFFER *firstbp = NULL;     /* ptr to first buffer in cmd line */
  195.     int viewflag;            /* are we starting in view mode? */
  196.     int gotoflag;            /* do we need to goto a line at start? */
  197.     int gline;            /* if so, what line? */
  198.     int searchflag;         /* Do we need to search at start? */
  199.     int errflag;            /* C error processing? */
  200.     VDESC vd;            /* variable num/type */
  201.     char bname[NBUFN];        /* buffer name of file to read */
  202.  
  203. #if    CRYPT
  204.     int cryptflag;            /* encrypting on the way in? */
  205.     char ekey[NPAT];        /* startup encryption key */
  206. #endif
  207.     CONST NOSHARE extern *pathname[]; /* startup file path/name array */
  208.  
  209.     viewflag = FALSE;    /* view mode defaults off in command line */
  210.     gotoflag = FALSE;    /* set to off to begin with */
  211.     searchflag = FALSE;    /* set to off to begin with */
  212.     firstfile = TRUE;    /* no file to edit yet */
  213.     startflag = FALSE;    /* startup file not executed yet */
  214.     errflag = FALSE;    /* not doing C error parsing */
  215.     exec_error = FALSE;    /* no macro error pending */
  216. #if    CRYPT
  217.     cryptflag = FALSE;    /* no encryption by default */
  218. #endif
  219.     disphigh = FALSE;    /* don't escape high bit characters */
  220.     lterm[0] = 0;        /* standard line terminators */
  221.  
  222.     /* Parse a command line */
  223.     for (carg = 1; carg < argc; ++carg) {
  224.  
  225.         /* Process Switches */
  226. #if WMCS
  227.         if (argv[carg][0] == ':') {
  228. #else
  229.         if (argv[carg][0] == '-') {
  230. #endif
  231.             switch (argv[carg][1]) {
  232.                 /* Process Startup macroes */
  233.                 case 'c':    /* -c for changable file */
  234.                 case 'C':
  235.                     viewflag = FALSE;
  236.                     break;
  237.                 case 'e':    /* -e process error file */
  238.                 case 'E':
  239.                     errflag = TRUE;
  240.                     break;
  241.                 case 'g':    /* -g for initial goto */
  242.                 case 'G':
  243.                     gotoflag = TRUE;
  244.                     gline = asc_int(&argv[carg][2]);
  245.                     break;
  246.                 case 'i':    /* -i<var> <value> set an initial */
  247.                 case 'I':    /* value for a variable */
  248.                     bytecopy(bname, &argv[carg][2], NVSIZE);
  249.                     findvar(bname, &vd, NVSIZE + 1);
  250.                     if (vd.v_type == -1) {
  251.                         mlwrite(TEXT52, bname);
  252. /*                            "%%No such variable as '%s'" */
  253.                         break;
  254.                     }
  255.                     svar(&vd, argv[++carg]);
  256.                     break;
  257. #if    CRYPT
  258.                 case 'k':    /* -k<key> for code key */
  259.                 case 'K':
  260.                     cryptflag = TRUE;
  261.                     strcpy(ekey, &argv[carg][2]);
  262.                     break;
  263. #endif
  264.                 case 'r':    /* -r restrictive use */
  265.                 case 'R':
  266.                     restflag = TRUE;
  267.                     break;
  268.                 case 's':    /* -s for initial search string */
  269.                 case 'S':
  270.                     searchflag = TRUE;
  271.                     bytecopy(pat,&argv[carg][2],NPAT);
  272.                     setjtable();
  273.                     break;
  274.                 case 'v':    /* -v for View File */
  275.                 case 'V':
  276.                     viewflag = TRUE;
  277.                     break;
  278.                 default:    /* unknown switch */
  279.                     /* ignore this for now */
  280.                     break;
  281.             }
  282.  
  283.         } else if (argv[carg][0]== '@') {
  284.  
  285.             /* Process Startup macroes */
  286.             if (startup(&argv[carg][1]) == TRUE)
  287.                 /* don't execute emacs.rc */
  288.                 startflag = TRUE;
  289.  
  290. #if WINDOW_MSWIN32
  291.         } else if ((argv[carg][0] != ' ') ||
  292.                            (argv[carg][1] != '\0')) {
  293.             /* WinNT PDK2 causes spurious space arguments */
  294. #else
  295.         } else {
  296. #endif
  297.             /* Process an input file */
  298.  
  299.             /* set up a buffer for this file */
  300.             makename(bname, argv[carg]);
  301.             unqname(bname);
  302.  
  303.             /* set this to inactive */
  304.             bp = bfind(bname, TRUE, 0);
  305.             strcpy(bp->b_fname, argv[carg]);
  306. #if     WINDOW_MSWIN
  307.             fullpathname (bp->b_fname, NFILEN);
  308. #endif
  309.             bp->b_active = FALSE;
  310.             if (firstfile) {
  311.                 firstbp = bp;
  312.                 firstfile = FALSE;
  313.             }
  314.  
  315.             /* set the modes appropriatly */
  316.             if (viewflag)
  317.                 bp->b_mode |= MDVIEW;
  318. #if    CRYPT
  319.             if (cryptflag) {
  320.                 bp->b_mode |= MDCRYPT;
  321.                 crypt((char *)NULL, 0);
  322.                 crypt(ekey, strlen(ekey));
  323.                 bytecopy(bp->b_key, ekey, NPAT);
  324.             }
  325. #endif
  326.         }
  327.     }
  328.  
  329.     /* if we are C error parsing... run it! */
  330.     if (errflag) {
  331.         if (startup("error.cmd") == TRUE)
  332.             startflag = TRUE;
  333.     }
  334.  
  335.     /* if invoked with no other startup files,
  336.        run the system startup file here */
  337.     if (firstflag && startflag == FALSE)
  338.         startup("");
  339.  
  340.     /* if there are any files to read, read the first one! */
  341.     if (firstflag) {
  342.         bp = bfind(mainbuf, FALSE, 0);
  343.         if (firstfile == FALSE && (gflags & GFREAD)) {
  344.             swbuffer(firstbp);
  345.             curbp->b_mode |= gmode;
  346.             update(TRUE);
  347.             mlwrite(lastmesg);
  348.             zotbuf(bp);
  349.         } else
  350.             bp->b_mode |= gmode;
  351.     } else {
  352.         swbuffer(firstbp);
  353.         curbp->b_mode |= gmode;
  354.         update(TRUE);
  355.         mlwrite(lastmesg);
  356.     }
  357.  
  358.  
  359.     /* Deal with startup gotos and searches */
  360.     if (gotoflag && searchflag) {
  361.         update(FALSE);
  362.         mlwrite(TEXT101);
  363. /*            "[Can not search and goto at the same time!]" */
  364.     }
  365.     else if (gotoflag) {
  366.         if (gotoline(TRUE,gline) == FALSE) {
  367.             update(FALSE);
  368.             mlwrite(TEXT102);
  369. /*                "[Bogus goto argument]" */
  370.         }
  371.     } else if (searchflag) {
  372.         if (forwhunt(FALSE, 0) == FALSE)
  373.             update(FALSE);
  374.     }
  375.  
  376. }
  377.  
  378. #if     WINDOW_MSWIN
  379. #define GETBASEKEY getbasekey
  380. static int PASCAL NEAR getbasekey()
  381. {
  382.     register int c;
  383.  
  384.     notquiescent = -1;  /* will be <= 0 only if getkey() is called
  385.                directly from editloop(). This is used to
  386.                restrict some windows-specific actions
  387.                (menus, sizing, etc...) when not called from
  388.                the lowest level of the editor */
  389.     c = getkey();
  390.     notquiescent = 1;
  391.     return c;
  392. }
  393. #else
  394. #define GETBASEKEY getkey
  395. #endif
  396.  
  397. /*
  398.     This is called to let the user edit something.    Note that if you
  399.     arrange to be able to call this from a macro, you will have
  400.     invented the "recursive-edit" function.
  401. */
  402.  
  403. PASCAL NEAR editloop()
  404.  
  405. {
  406.     register int c;        /* command character */
  407.     register int f;     /* default flag */
  408.     register int n;     /* numeric repeat count */
  409.     register int mflag;    /* negative flag on repeat */
  410.     register int basec;    /* c stripped of meta character */
  411.     register int oldflag;    /* old last flag value */
  412.     char time[6];        /* current display time */
  413.  
  414.     /* setup to process commands */
  415.     lastflag = 0;        /* Fake last flags.    */
  416.  
  417. loop:
  418.     /* if a macro error is pending, wait for a character */
  419.     if (exec_error) {
  420. #if     WINDOW_MSWIN
  421.                 mlhistory();
  422. #else
  423.         mlforce(TEXT227);
  424. /*            "\n--- Press any key to Continue ---" */
  425.         tgetc();
  426. #endif
  427.         sgarbf = TRUE;
  428.         update(FALSE);
  429.         mlferase();
  430.         exec_error = FALSE;
  431.     }
  432.  
  433.     /* if we were called as a subroutine and want to leave, do so */
  434.     if (eexitflag)
  435.         return(eexitval);
  436.  
  437.     /* execute the "command" macro...normally null */
  438.     oldflag = lastflag;    /* preserve lastflag through this */
  439.     execkey(&cmdhook, FALSE, 1);
  440.     lastflag = oldflag;
  441.  
  442. #if    VMS
  443.     if (pending_msg) {
  444.         makelit(brdcstbuf);
  445.         mlwrite(brdcstbuf);
  446.         pending_msg = FALSE;
  447.     }
  448. #endif
  449.  
  450.     /* update time on the bottom modeline? */
  451.     if (timeflag)
  452. #if TYPEAH || WINDOW_MSWIN
  453.             if (!typahead())
  454. #endif
  455.             {
  456.         getdtime(time);
  457.         if (strcmp(lasttime, time) != 0)
  458.             upmode();
  459.     }
  460.  
  461.     /* update position on current modeline? */
  462.     if (posflag)
  463. #if TYPEAH || WINDOW_MSWIN
  464.             if (!typahead())
  465. #endif
  466.         upmode();
  467.  
  468.     /* Fix up the screen    */
  469.     update(FALSE);
  470.  
  471.     /* get the next command from the keyboard */
  472.     discmd = TRUE;
  473.     disinp = TRUE;
  474.     c = GETBASEKEY();
  475.  
  476.     /* if there is something on the command line, clear it */
  477.     if (mpresf != FALSE) {
  478.         mlerase();
  479.         update(FALSE);
  480.     }
  481.  
  482.     /* override the arguments if prefixed */
  483.     if (prefix) {
  484.         if (islower(c & 255))
  485.             c = (c & ~255) | upperc(c & 255);
  486.         c |= prefix;
  487.         f = predef;
  488.         n = prenum;
  489.         prefix = 0;
  490.     } else {
  491.         f = FALSE;
  492.         n = 1;
  493.     }
  494.  
  495.     /* do META-# processing if needed */
  496.  
  497.     basec = c & ~META;        /* strip meta char off if there */
  498.     if ((c & META) && ((basec >= '0' && basec <= '9') || basec == '-') &&
  499.         (getbind(c) == NULL)) {
  500.         f = TRUE;        /* there is a # arg */
  501.         n = 0;            /* start with a zero default */
  502.         mflag = 1;        /* current minus flag */
  503.         c = basec;        /* strip the META */
  504.         while ((c >= '0' && c <= '9') || (c == '-')) {
  505.             if (c == '-') {
  506.                 /* already hit a minus or digit? */
  507.                 if ((mflag == -1) || (n != 0))
  508.                     break;
  509.                 mflag = -1;
  510.             } else {
  511.                 n = n * 10 + (c - '0');
  512.             }
  513.             if ((n == 0) && (mflag == -1))    /* lonely - */
  514.                 mlwrite("Arg:");
  515.             else
  516.                 mlwrite("Arg: %d",n * mflag);
  517.  
  518.             c = GETBASEKEY();    /* get the next key */
  519.         }
  520.         n = n * mflag;    /* figure in the sign */
  521.     }
  522.  
  523.     /* do ^U repeat argument processing */
  524.  
  525.     if (c == reptc) {           /* ^U, start argument   */
  526.         f = TRUE;
  527.         n = 4;                /* with argument of 4 */
  528.         mflag = 0;            /* that can be discarded. */
  529.         mlwrite("Arg: 4");
  530.         while ((c=GETBASEKEY()) >='0' && c<='9' || c==reptc || c=='-') {
  531.             if (c == reptc)
  532.                 if ((n > 0) == ((n*4) > 0))
  533.                     n = n*4;
  534.                 else
  535.                     n = 1;
  536.             /*
  537.              * If dash, and start of argument string, set arg.
  538.              * to -1.  Otherwise, insert it.
  539.              */
  540.             else if (c == '-') {
  541.                 if (mflag)
  542.                     break;
  543.                 n = 0;
  544.                 mflag = -1;
  545.             }
  546.             /*
  547.              * If first digit entered, replace previous argument
  548.              * with digit and set sign.  Otherwise, append to arg.
  549.              */
  550.             else {
  551.                 if (!mflag) {
  552.                     n = 0;
  553.                     mflag = 1;
  554.                 }
  555.                 n = 10*n + c - '0';
  556.             }
  557.             mlwrite("Arg: %d", (mflag >=0) ? n : (n ? -n : -1));
  558.         }
  559.         /*
  560.          * Make arguments preceded by a minus sign negative and change
  561.          * the special argument "^U -" to an effective "^U -1".
  562.          */
  563.         if (mflag == -1) {
  564.             if (n == 0)
  565.                 n++;
  566.             n = -n;
  567.         }
  568.     }
  569.  
  570.     /* and execute the command */
  571.     execute(c, f, n);
  572.     goto loop;
  573. }
  574.  
  575. /*
  576.  * Initialize all of the buffers, windows and screens. The buffer name is
  577.  * passed down as an argument, because the main routine may have been told
  578.  * to read in a file by default, and we want the buffer name to be right.
  579.  */
  580.  
  581. PASCAL NEAR edinit(bname)
  582.  
  583. char bname[];    /* name of buffer to initialize */
  584.  
  585. {
  586.     register BUFFER *bp;
  587.     register int index;
  588.  
  589.     /* init the kill ring */
  590.     for (index = 0; index < NRING; index++) {
  591.         kbufp[index] = (KILL *)NULL;
  592.         kbufh[index] = (KILL *)NULL;
  593.         kskip[index] = 0;
  594.         kused[index] = KBLOCK;
  595.     }
  596.     kill_index = 0;
  597.  
  598.     /* initialize some important globals */
  599.  
  600.     readhook.k_ptr.fp = nullproc;    /* set internal hooks to OFF */
  601.     readhook.k_type = BINDFNC;
  602.     wraphook.k_ptr.fp = wrapword;
  603.     wraphook.k_type = BINDFNC;
  604.     cmdhook.k_ptr.fp = nullproc;
  605.     cmdhook.k_type = BINDFNC;
  606.     writehook.k_ptr.fp = nullproc;
  607.     writehook.k_type = BINDFNC;
  608.     bufhook.k_ptr.fp = nullproc;
  609.     bufhook.k_type = BINDFNC;
  610.     exbhook.k_ptr.fp = nullproc;
  611.     exbhook.k_type = BINDFNC;
  612.  
  613.     /* allocate the first buffer */
  614.     bp = bfind(bname, TRUE, 0);        /* First buffer     */
  615.     blistp = bfind("[Buffers]", TRUE, BFINVS); /* Buffer list buffer    */
  616.     slistp = bfind("[Screens]", TRUE, BFINVS); /* Buffer list buffer    */
  617.     if (bp==NULL || blistp==NULL)
  618.         meexit(1);
  619.  
  620.     /* and allocate the default screen */
  621.     first_screen = (SCREEN *)NULL;
  622.     init_screen("MAIN", bp);
  623.     if (first_screen == (SCREEN *)NULL)
  624.         meexit(1);
  625.  
  626.     /* set the current default screen/buffer/window */
  627.     curbp = bp;
  628.     curwp = wheadp = first_screen->s_cur_window = first_screen->s_first_window;
  629. }
  630.  
  631. /*
  632.  * This is the general command execution routine. It handles the fake binding
  633.  * of all the keys to "self-insert". It also clears out the "thisflag" word,
  634.  * and arranges to move it to the "lastflag", so that the next command can
  635.  * look at it. Return the status of command.
  636.  */
  637. PASCAL NEAR execute(c, f, n)
  638.  
  639. int c;        /* key to execute */
  640. int f;        /* prefix argument flag */
  641. int n;        /* prefix value */
  642.  
  643. {
  644.     register int status;
  645.     KEYTAB *key;        /* key entry to execute */
  646. #if    DBCS
  647.     int schar;        /* second key in 2 byte sequence */
  648. #endif
  649.  
  650. #if    WINDOW_MSWIN
  651.     /* if it is a menu command, go for it... */
  652.     if ((c & MENU) == MENU) {
  653.         thisflag = 0;
  654.         status = execmenu(f, n);
  655.         lastflag = thisflag;
  656.         return(status);
  657.     }
  658. #endif
  659.     /* if the keystroke is a bound function...do it */
  660.     key = getbind(c);
  661.     if (key != NULL) {
  662.  
  663.         /* Don't reset the function type flags on a prefix */
  664.         if ((key->k_type == BINDFNC) &&
  665.             ((key->k_ptr.fp == meta) || (key->k_ptr.fp == cex)))
  666.             status = execkey(key, f, n);
  667.         else {
  668.             thisflag = 0;
  669.             status = execkey(key, f, n);
  670.             lastflag = thisflag;
  671.         }
  672.  
  673.         return(status);
  674.     }
  675.  
  676.     /*
  677.      * If a space was typed, fill column is defined, the argument is non-
  678.      * negative, wrap mode is enabled, and we are now past fill column,
  679.      * and we are not read-only, perform word wrap.
  680.      */
  681.     if (c == ' ' && (curwp->w_bufp->b_mode & MDWRAP) && fillcol > 0 &&
  682.         n >= 0 && getccol(FALSE) > fillcol &&
  683.         (curwp->w_bufp->b_mode & MDVIEW) == FALSE)
  684.         execkey(&wraphook, FALSE, 1);
  685.  
  686.     if ((c>=0x20 && c<=0xFF)) {    /* Self inserting.    */
  687.         if (n <= 0) {            /* Fenceposts.        */
  688.             lastflag = 0;
  689.             return(n<0 ? FALSE : TRUE);
  690.         }
  691.         thisflag = 0;            /* For the future.    */
  692.  
  693.         /* replace or overwrite mode, not at the end of a string */
  694.         if (curwp->w_bufp->b_mode & (MDREPL | MDOVER) &&
  695.             curwp->w_doto < curwp->w_dotp->l_used) {
  696.  
  697.             /* if we are in replace mode, or
  698.                (next char is not a tab or we are at a tab stop) */
  699.             if (curwp->w_bufp->b_mode & MDREPL ||
  700.                 (lgetc(curwp->w_dotp, curwp->w_doto) != '\t' ||
  701.                 getccol(FALSE) % tabsize == (tabsize - 1)))
  702. /*                (curwp->w_doto) % tabsize == (tabsize - 1)))
  703.                 ^^^^^^^^^^^^^ these needs to be the column,
  704.             DML          not the offset! */
  705.                         ldelete(1L, FALSE);
  706.         }
  707.  
  708.         /* do the appropriate insertion */
  709.         if (c == '}' && (curbp->b_mode & MDCMOD) != 0)
  710.             status = insbrace(n, c);
  711.         else if (c == '#' && (curbp->b_mode & MDCMOD) != 0)
  712.             status = inspound();
  713. #if    DBCS
  714.         else if (is2char(c)) {
  715.             schar = getkey();
  716.             status = TRUE;
  717.             while (n--) {
  718.                 if (linsert(1, c) == FALSE)
  719.                     status = FALSE;
  720.                 if (linsert(1, schar) == FALSE)
  721.                     status = FALSE;
  722.             }
  723.         }
  724. #endif
  725.  
  726.         else
  727.             status = linsert(n, c);
  728.  
  729.         /* check for CMODE fence matching */
  730.         if ((c == '}' || c == ')' || c == ']') &&
  731.                 (curbp->b_mode & MDCMOD) != 0)
  732.             fmatch(c);
  733.  
  734.         /* check auto-save mode */
  735.         if (curbp->b_mode & MDASAVE)
  736.             if (--gacount == 0) {
  737.                 /* and save the file if needed */
  738.                 upscreen(FALSE, 0);
  739.                 filesave(FALSE, 0);
  740.                 gacount = gasave;
  741.             }
  742.  
  743.         lastflag = thisflag;
  744.         return(status);
  745.     }
  746.     TTbeep();
  747.     mlwrite(TEXT19);        /* complain        */
  748. /*        "[Key not bound]" */
  749.     lastflag = 0;                /* Fake last flags.    */
  750.     return(FALSE);
  751. }
  752.  
  753. /*
  754.     Fancy quit command, as implemented by Norm. If the any buffer
  755. has changed do a write on that buffer and exit emacs, otherwise simply
  756. exit.
  757. */
  758.  
  759. PASCAL NEAR quickexit(f, n)
  760.  
  761. int f,n;    /* prefix flag and argument */
  762.  
  763. {
  764.     register BUFFER *bp;    /* scanning pointer to buffers */
  765.     register BUFFER *oldcb; /* original current buffer */
  766.     register int status;
  767.  
  768.     oldcb = curbp;                /* save in case we fail */
  769.  
  770. #if    TIPC
  771.     mlwrite("\n\n");
  772. #endif
  773.     bp = bheadp;
  774.     while (bp != NULL) {
  775.         if ((bp->b_flag&BFCHG) != 0    /* Changed.        */
  776.         && (bp->b_flag&BFINVS) == 0) {    /* Real.        */
  777.             curbp = bp;        /* make that buffer cur */
  778.             mlwrite(TEXT103,bp->b_fname);
  779. /*                "[Saving %s]" */
  780.             mlwrite("\n");
  781.             if ((status = filesave(f, n)) != TRUE) {
  782.                 curbp = oldcb;    /* restore curbp */
  783.                 return(status);
  784.             }
  785.         }
  786.     bp = bp->b_bufp;            /* on to the next buffer */
  787.     }
  788.     quit(f, n);                /* conditionally quit    */
  789.     return(TRUE);
  790. }
  791.  
  792. /*
  793.  * Quit command. If an argument, always quit. Otherwise confirm if a buffer
  794.  * has been changed and not written out. Normally bound to "C-X C-C".
  795.  */
  796.  
  797. PASCAL NEAR quit(f, n)
  798.  
  799. int f,n;    /* prefix flag and argument */
  800.  
  801. {
  802.     register int status;    /* return status */
  803.  
  804.     if (f != FALSE        /* Argument forces it.    */
  805.     || anycb() == FALSE    /* All buffers clean or user says it's OK. */
  806.     || (status = mlyesno(TEXT104)) == TRUE) {
  807. /*                 "Modified buffers exist. Leave anyway" */
  808. #if    FILOCK
  809.         if (lockrel() != TRUE) {
  810.             TTputc('\n');
  811.             TTputc('\r');
  812.             TTclose();
  813.             TTkclose();
  814.             status = meexit(1);
  815.         }
  816. #endif
  817.         if (f)
  818.             status = meexit(n);
  819.         else
  820.             status = meexit(GOOD);
  821.     }
  822.     mlerase();
  823.     return(status);
  824. }
  825.  
  826. PASCAL NEAR meexit(status)
  827. int status;    /* return status of emacs */
  828. {
  829.     eexitflag = TRUE;    /* flag a program exit */
  830.     eexitval = status;
  831.  
  832.     /* and now.. we leave and let the main loop kill us */
  833.     return(TRUE);
  834. }
  835.  
  836. /*
  837.  * Begin a keyboard macro.
  838.  * Error if not at the top level in keyboard processing. Set up variables and
  839.  * return.
  840.  */
  841.  
  842. PASCAL NEAR ctlxlp(f, n)
  843.  
  844. int f,n;    /* prefix flag and argument */
  845.  
  846. {
  847.     if (kbdmode != STOP) {
  848.         mlwrite(TEXT105);
  849. /*            "%%Macro already active" */
  850.         return(FALSE);
  851.     }
  852.     mlwrite(TEXT106);
  853. /*        "[Start macro]" */
  854.     kbdptr = &kbdm[0];
  855.     kbdend = kbdptr;
  856.     kbdmode = RECORD;
  857.     return(TRUE);
  858. }
  859.  
  860. /*
  861.  * End keyboard macro. Check for the same limit conditions as the above
  862.  * routine. Set up the variables and return to the caller.
  863.  */
  864.  
  865. PASCAL NEAR ctlxrp(f, n)
  866.  
  867. int f,n;    /* prefix flag and argument */
  868.  
  869. {
  870.     if (kbdmode == STOP) {
  871.         mlwrite(TEXT107);
  872. /*            "%%Macro not active" */
  873.         return(FALSE);
  874.     }
  875.     if (kbdmode == RECORD) {
  876.         mlwrite(TEXT108);
  877. /*            "[End macro]" */
  878.         kbdmode = STOP;
  879.     }
  880.     return(TRUE);
  881. }
  882.  
  883. /*
  884.  * Execute a macro.
  885.  * The command argument is the number of times to loop. Quit as soon as a
  886.  * command gets an error. Return TRUE if all ok, else FALSE.
  887.  */
  888.  
  889. PASCAL NEAR ctlxe(f, n)
  890.  
  891. int f,n;    /* prefix flag and argument */
  892.  
  893. {
  894.     if (kbdmode != STOP) {
  895.         mlwrite(TEXT105);
  896. /*            "%%Macro already active" */
  897.         return(FALSE);
  898.     }
  899.     if (n <= 0)
  900.         return(TRUE);
  901.     kbdrep = n;        /* remember how many times to execute */
  902.     kbdmode = PLAY;     /* start us in play mode */
  903.     kbdptr = &kbdm[0];    /*    at the beginning */
  904.     return(TRUE);
  905. }
  906.  
  907. /*
  908.  * Abort.
  909.  * Beep the beeper. Kill off any keyboard macro, etc., that is in progress.
  910.  * Sometimes called as a routine, to do general aborting of stuff.
  911.  */
  912.  
  913. PASCAL NEAR ctrlg(f, n)
  914.  
  915. int f,n;    /* prefix flag and argument */
  916.  
  917. {
  918.     TTbeep();
  919.     kbdmode = STOP;
  920.     mlwrite(TEXT8);
  921. /*        "[Aborted]" */
  922.     return(ABORT);
  923. }
  924.  
  925. /* tell the user that this command is illegal while we are in
  926.    VIEW (read-only) mode                */
  927.  
  928. PASCAL NEAR rdonly()
  929.  
  930. {
  931.     TTbeep();
  932.     mlwrite(TEXT109);
  933. /*        "[Key illegal in VIEW mode]" */
  934.     return(FALSE);
  935. }
  936.  
  937. PASCAL NEAR resterr()
  938.  
  939. {
  940.     TTbeep();
  941.     mlwrite(TEXT110);
  942. /*        "[That command is RESTRICTED]" */
  943.     return(FALSE);
  944. }
  945.  
  946. PASCAL NEAR nullproc(f, n)    /* user function that does NOTHING */
  947.  
  948. int n, f;    /* yes, these are default and never used.. but MUST be here */
  949.  
  950. {
  951. }
  952.  
  953. PASCAL NEAR meta(f, n)    /* set META prefixing pending */
  954.  
  955. int f, n;    /* prefix flag and argument */
  956.  
  957. {
  958.     prefix |= META;
  959.     prenum = n;
  960.     predef = f;
  961.     return(TRUE);
  962. }
  963.  
  964. PASCAL NEAR cex(f, n)    /* set ^X prefixing pending */
  965.  
  966. int f, n;    /* prefix flag and argument */
  967.  
  968. {
  969.     prefix |= CTLX;
  970.     prenum = n;
  971.     predef = f;
  972.     return(TRUE);
  973. }
  974.  
  975. PASCAL NEAR unarg()    /* dummy function for binding to universal-argument */
  976. {
  977. }
  978.  
  979. /*    bytecopy:    copy a string...with length restrictions
  980.             ALWAYS null terminate
  981. */
  982.  
  983. char *PASCAL NEAR bytecopy(dst, src, maxlen)
  984.  
  985. char *dst;    /* destination of copied string */
  986. char *src;    /* source */
  987. int maxlen;    /* maximum length */
  988.  
  989. {
  990.     char *dptr;    /* ptr into dst */
  991.  
  992.     dptr = dst;
  993.     while (*src && (maxlen-- > 0))
  994.         *dptr++ = *src++;
  995.     *dptr = 0;
  996.     return(dst);
  997. }
  998.  
  999. /*    copystr:    make another copy of the argument
  1000.  
  1001. */
  1002.  
  1003. char *PASCAL NEAR copystr(sp)
  1004.  
  1005. char *sp;    /* string to copy */
  1006.  
  1007. {
  1008.     char *dp;    /* copy of string */
  1009.  
  1010.     /* make room! */
  1011.     dp = malloc(strlen(sp)+1);
  1012.     if (dp == NULL)
  1013.         return(NULL);
  1014.     strcpy(dp, sp);
  1015.     return(dp);
  1016. }
  1017.  
  1018. /*****        Compiler specific Library functions    ****/
  1019.  
  1020. #if    RAMSIZE
  1021. /*    These routines will allow me to track memory usage by placing
  1022.     a layer on top of the standard system malloc() and free() calls.
  1023.     with this code defined, the environment variable, $RAM, will
  1024.     report on the number of bytes allocated via malloc.
  1025.  
  1026.     with SHOWRAM defined, the number is also posted on the
  1027.     end of the bottom mode line and is updated whenever it is changed.
  1028. */
  1029.  
  1030. #undef    malloc
  1031. #undef    free
  1032.  
  1033. #if     VMS & OPTMEM        /* these routines are faster! */
  1034. #define    malloc    VAXC$MALLOC_OPT
  1035. #define free    VAXC$FREE_OPT
  1036. #endif
  1037.  
  1038. char *allocate(nbytes)    /* allocate nbytes and track */
  1039.  
  1040. unsigned nbytes;    /* # of bytes to allocate */
  1041.  
  1042. {
  1043.     char *mp;    /* ptr returned from malloc */
  1044.     char *malloc();
  1045.     FILE *track;    /* malloc track file */
  1046.  
  1047.     mp = malloc(nbytes);
  1048.  
  1049. #if    RAMTRCK
  1050.     track = fopen("malloc.dat", "a");
  1051.     fprintf(track, "Allocating %u bytes at %u:%u\n", nbytes,
  1052.             FP_SEG(mp), FP_OFF(mp));
  1053.     fclose(track);
  1054. #endif
  1055.  
  1056.     if (mp) {
  1057. #if    0
  1058.         envram += nbytes;
  1059. #else
  1060.         envram += 1;
  1061. #endif
  1062. #if    RAMSHOW
  1063.         dspram();
  1064. #endif
  1065.     }
  1066.  
  1067.     return(mp);
  1068. }
  1069.  
  1070. release(mp)    /* release malloced memory and track */
  1071.  
  1072. char *mp;    /* chunk of RAM to release */
  1073.  
  1074. {
  1075.     unsigned *lp;    /* ptr to the long containing the block size */
  1076. #if    RAMTRCK
  1077.     FILE *track;    /* malloc track file */
  1078.  
  1079.     track = fopen("malloc.dat", "a");
  1080.     fprintf(track, "Freeing %u:%u\n",
  1081.             FP_SEG(mp), FP_OFF(mp));
  1082.     fclose(track);
  1083. #endif
  1084.  
  1085.     if (mp) {
  1086.         /* update amount of ram currently malloced */
  1087. #if    0
  1088.         lp = ((unsigned *)mp) - 1;
  1089.         envram -= (long)*lp - 2;
  1090. #else
  1091.         envram -= 1;
  1092. #endif
  1093.         free(mp);
  1094. #if    RAMSHOW
  1095.         dspram();
  1096. #endif
  1097.     }
  1098. }
  1099.  
  1100. #if    RAMSHOW
  1101. dspram()    /* display the amount of RAM currently malloced */
  1102.  
  1103. {
  1104.     char mbuf[20];
  1105.     char *sp;
  1106.  
  1107.     TTmove(term.t_nrow - 1, 70);
  1108. #if    COLOR
  1109.     TTforg(7);
  1110.     TTbacg(0);
  1111. #endif
  1112.     sprintf(mbuf, "[%lu]", envram);
  1113.     sp = &mbuf[0];
  1114.     while (*sp)
  1115.         TTputc(*sp++);
  1116.     TTmove(term.t_nrow, 0);
  1117.     movecursor(term.t_nrow, 0);
  1118. }
  1119. #endif
  1120. #endif
  1121.